// [[ OnflowGenerator File UUID: d08f56ae282a40a7886d7adc4124792c  ]]
package com.itdct.onflow.system.api.manager

import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import com.itdct.onflow.core.extend.logError
import com.itdct.onflow.core.extend.logInfo
import com.itdct.onflow.core.extend.saveBatch
import com.itdct.onflow.core.extend.selectAll
import com.itdct.onflow.system.api.entity.SysApiEntity
import com.itdct.onflow.system.api.entity.SysApiMenuEntity
import com.itdct.onflow.system.api.global.ApiGlobalMap
import com.itdct.onflow.system.api.global.ApiRoleLock
import com.itdct.onflow.system.api.mapper.SysApiMapper
import com.itdct.onflow.system.api.mapper.SysApiMenuMapper
import com.itdct.onflow.system.common.base.BaseManager
import com.itdct.onflow.system.role.mapper.SysRoleMapper
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.mvc.method.RequestMappingInfo
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

/**
 * @author DCTANT
 * @date 2025-09-01 15:08:45
 * @version 1.0
 * @description 系统接口中间层
 */
@Component
class SysApiManager(
    @Qualifier("requestMappingHandlerMapping")
    val handlerMapping: RequestMappingHandlerMapping,
    val sysApiMapper: SysApiMapper,
    @Value("\${server.servlet.context-path}")
    val contextPath: String,
    val sysApiMenuMapper: SysApiMenuMapper,
    val sysRoleMapper: SysRoleMapper
) : BaseManager() {

    fun refreshApi() {
        if (!onflowConfig.initApi) {
            return
        }
        val apiList = getApiList()
        val queryWrapper = KtQueryWrapper(SysApiEntity::class.java)
        queryWrapper.eq(SysApiEntity::platformType, onflowConfig.platformType)

        val insertList = ArrayList<SysApiEntity>()
        val dbApiList = sysApiMapper.selectList(queryWrapper)

        val checkPermission = onflowConfig.checkPermission

        for (api in apiList) {
            val dbApi = dbApiList.find { it.url == api.url && it.method == api.method }
            if (dbApi == null) {
                // INFO: Zhouwx: 2025/4/27 数据库中没有，需要新增
                insertList.add(api)

                if (checkPermission) {
                    val apiKey = ApiGlobalMap.getApiKey(api)
                    ApiGlobalMap.apiMap[apiKey] = api
                }
            } else {
                if (checkPermission) {
                    val apiKey = ApiGlobalMap.getApiKey(dbApi)
                    ApiGlobalMap.apiMap[apiKey] = dbApi
                }
            }
        }
        if (insertList.isNotEmpty()) {
            logInfo("发现新增${insertList.size}个接口")
            sysApiMapper.saveBatch(insertList)
        }

        val deleteList = ArrayList<SysApiEntity>()
        for (dbApi in dbApiList) {
            val api = apiList.find { it.url == dbApi.url && it.method == dbApi.method }
            if (api == null) {
                // INFO: Zhouwx: 2025/4/27 数据库中有，但是现在没有，需要删除
                deleteList.add(dbApi)
            } else {
                if (checkPermission) {
                    val apiKey = ApiGlobalMap.getApiKey(dbApi)
                    ApiGlobalMap.apiMap[apiKey] = dbApi
                }
            }
        }

        if (deleteList.isNotEmpty()) {
            logInfo("发现删除${insertList.size}个接口")
            sysApiMapper.deleteBatchIds(deleteList.map { it.id!! })

            val relQueryWrapper = KtQueryWrapper(SysApiMenuEntity::class.java)
            relQueryWrapper.`in`(SysApiMenuEntity::apiId, deleteList.map { it.id!! })
            sysApiMenuMapper.delete(relQueryWrapper)
        }
    }

    fun getApiList(): List<SysApiEntity> {
        val apiList = mutableListOf<SysApiEntity>()
        val handlerMethods = handlerMapping.handlerMethods
        for ((mappingInfo, handlerMethod) in handlerMethods) {
            val method = handlerMethod.method

            method.getAnnotation(RequestMapping::class.java)?.also {
                for (requestMethod in it.method) {
                    val name = requestMethod.name
                    val api = initMethodWithMethodName(name, mappingInfo, handlerMethod)
                    apiList.addAll(api)
                }
            }
            method.getAnnotation(GetMapping::class.java)?.also {
                val api = initMethodWithMethodName("GET", mappingInfo, handlerMethod)
                apiList.addAll(api)
            }
            method.getAnnotation(PostMapping::class.java)?.also {
                val api = initMethodWithMethodName("POST", mappingInfo, handlerMethod)
                apiList.addAll(api)
            }
            method.getAnnotation(PutMapping::class.java)?.also {
                val api = initMethodWithMethodName("PUT", mappingInfo, handlerMethod)
                apiList.addAll(api)
            }
            method.getAnnotation(org.springframework.web.bind.annotation.DeleteMapping::class.java)?.also {
                val api = initMethodWithMethodName("DELETE", mappingInfo, handlerMethod)
                apiList.addAll(api)
            }
            method.getAnnotation(org.springframework.web.bind.annotation.PatchMapping::class.java)?.also {
                val api = initMethodWithMethodName("PATCH", mappingInfo, handlerMethod)
                apiList.addAll(api)
            }

        }
        val distinctApiList = apiList.distinctBy { it.method + "://" + it.url }
        return distinctApiList
    }

    fun initMethodWithMethodName(methodName: String, mappingInfo: RequestMappingInfo, handlerMethod: HandlerMethod): List<SysApiEntity> {
        val apiEntities = ArrayList<SysApiEntity>()
        for (patternValue in mappingInfo.patternValues) {
            val entity = initMethodWithPatternValue(
                patternValue,
                methodName,
                mappingInfo,
                handlerMethod
            )
            apiEntities.add(entity)
        }
        return apiEntities
    }

    private fun initMethodWithPatternValue(patternValue: String, methodName: String, mappingInfo: RequestMappingInfo, handlerMethod: HandlerMethod): SysApiEntity {
        val method = handlerMethod.method
        val apiEntity = SysApiEntity()

        val beanType = handlerMethod.beanType
        var moduleName = ""
        beanType.getAnnotation(Tag::class.java)?.also {
            moduleName = it.name
        }

        apiEntity.moduleName = moduleName
        apiEntity.platformType = onflowConfig.platformType
        apiEntity.name = method.getAnnotation(Operation::class.java)?.summary ?: ""
        apiEntity.url = "$contextPath$patternValue"
        apiEntity.method = methodName
        apiEntity.remark = method.getAnnotation(Operation::class.java).description
        onflowConfig.login.noLimitApis.forEach {
            if (apiEntity.url.contains(it)) {
                // INFO: Zhouwx: 2025/9/3 登录接口默认不控制
                apiEntity.controlType = 0
            } else {
                // INFO: Zhouwx: 2025/9/3 接口默认登录控制
                apiEntity.controlType = 10
            }
        }
        return apiEntity
    }

    fun refreshRoleApi() {
        ApiRoleLock.lock.lock()
        try {
            logInfo("开始刷新角色接口权限")
            val roleEntities = sysRoleMapper.selectAll()
            ApiGlobalMap.roleApiMap.clear()
            for (roleEntity in roleEntities) {
                val apis: HashSet<SysApiEntity> = sysApiMapper.findRoleApis(roleEntity.id!!)
                val apiKeys = HashSet<String>()
                for (api in apis) {
                    val apiKey = ApiGlobalMap.getApiKey(api)
                    apiKeys.add(apiKey)
                }
                ApiGlobalMap.roleApiMap[roleEntity.id!!] = apiKeys
            }
            logInfo("结束刷新角色接口权限")
        } catch (e: Exception) {
            logError(e)
        } finally {
            ApiRoleLock.lock.unlock()
        }
    }

}